package com.uaf.vxalipaybill.provider.lisener;

import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.security.Security;
import java.util.Date;
import java.util.Properties;

import javax.mail.Address;
import javax.mail.FetchProfile;
import javax.mail.Flags;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.UIDFolder;
import javax.mail.URLName;
import javax.mail.internet.MimeMessage;
import javax.mail.search.AndTerm;
import javax.mail.search.ComparisonTerm;
import javax.mail.search.FlagTerm;
import javax.mail.search.SearchTerm;
import javax.mail.search.SentDateTerm;

import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import com.sun.mail.imap.IMAPFolder;
import com.sun.mail.util.MailSSLSocketFactory;
import com.uaf.common.date.utils.DateUtils;
import com.uaf.common.file.FileOperateUtil;
import com.uaf.http.HttpClientService;
import com.uaf.http.WebClientService;
import com.uaf.log.MySlf4j;
import com.uaf.redis.RedissonClientTemplate;
import com.uaf.vxalipaybill.provider.config.Config;
import com.uaf.vxalipaybill.provider.utils.EmailUtils;
import com.uaf.vxalipaybill.provider.utils.VxAlipayBillConstantUtils;

/**
 * @filename MailListener.java
 * @description 邮件监听
 * @author 马庆智
 * @date 2021/9/15 11:06
 */
@Component
public class MailListener {
	public static final int cache = 10 * 1024;

	@Autowired
	private Config config;
	@Autowired
	private HttpClientService httpClientService;

	/**redis工具*/
	@Autowired
	private RedissonClientTemplate redissonClientTemplate;
	@Autowired
	private WebClientService webClientService;

	private IMAPFolder folder = null;

	private Store store;

	/**
	 * 功能描述  初始化邮箱连接
	 * @author 马庆智
	 * @date 2021/9/9 14:40
	 */
	public Store initConnect() throws Exception {
		/** 1.设置SSL连接、邮件环境 */
		Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
		Properties props = System.getProperties();
		MailSSLSocketFactory sf = null;
		try {
			sf = new MailSSLSocketFactory();
			sf.setTrustAllHosts(true);
		} catch (GeneralSecurityException e1) {
			e1.printStackTrace();
		}
		props.put("mail.imap.ssl.enable", "true");
		props.put("mail.imap.ssl.socketFactory", sf);
		props.put("mail.imap.socketFactory.fallback", "false");
		props.put("mail.imap.port", config.port);
		props.put("mail.imap.socketFactory.port", config.port);
		props.put("mail.imap.auth", "true");

		/** 2.创建Session实例对象 */
		Session session = Session.getInstance(props);
		/** 3.设置连接邮件仓库的环境*/
		URLName url = new URLName(VxAlipayBillConstantUtils.IMAP_PROTOCOL, config.host, Integer.parseInt(config.port),
				null, config.userName, config.password);
		Store store = null;

		/** 4. 得到邮件仓库并连接 */
		store = session.getStore(url);
		store.connect();
		return store;
	}

	/**
	 * 功能描述 邮箱监听（获取未读邮件）
	 * @author 马庆智
	 * @date 2021/9/9 14:41
	 */
	@Scheduled(cron = "${vxalipaybill.mail.receiver.cron}")
	public void noop() {
		try {
			/**1.生成请求分布式Redisson锁,集群部署情况防止同份邮件被下载,保证同一时间的定时任务执行未读邮件只能有一个线程*/
			String lockKey = VxAlipayBillConstantUtils.LISTENE_EMIAL_REDIS_KEY.concat(config.userName);

			try {
				/**2.添加防止重复请求分布式Redisson锁*/
				redissonClientTemplate.lock(lockKey, VxAlipayBillConstantUtils.REDIS_LOCK_EXPIRE);
				MySlf4j.textDebug("监听邮箱未读的微信支付宝账单邮件,并下载到ftp文件服务器,开始下载");
				store = initConnect();
				Folder folders = store.getFolder("Inbox");
				folders.open(Folder.READ_WRITE);
				folder = (IMAPFolder) folders;
				/**获取分钟数内未读的邮件*/
				Message[] messages = getUnReadMessages();
				try {
					for (int i = 0; i < messages.length; i++) {
						MySlf4j.textInfo("邮箱对应的UUID:{0}", folder.getUID(messages[i]));
						/**获得邮件主题*/
						String subject = messages[i].getSubject();
						String receiveEmailAddress = EmailUtils.getAddressUrl((MimeMessage) messages[i]);
						MySlf4j.textInfo("邮箱对应的UUID:{0},subject:{1}", folder.getUID(messages[i]), subject);
						if (receiveEmailAddress.contains(config.vxSendEmail)) {
							dealVxBill(messages[i]);
						} else if (receiveEmailAddress.contains(config.alipaySendEmail)) {
							dealAlipayBill(messages[i]);
						} else {
							MySlf4j.textInfo("{0},不是微信支付宝发送的邮箱,设置成已经读取", receiveEmailAddress);
							messages[i].setFlag(Flags.Flag.SEEN, true);
						}
					}
				} catch (Exception e) {
					MySlf4j.textError("邮件下载,异常信息为{0}", MySlf4j.ExceptionToString(e));
				} finally {
					this.folder.close(true);
					this.store.close();
				}
			} catch (Exception e) {
				MySlf4j.textError("邮箱下载过程异常:{0}", MySlf4j.ExceptionToString(e));
			} finally {
				/**3.删除分布式Redisson锁*/
				MySlf4j.textDebug("监听邮箱未读的微信支付宝账单邮件,并下载到ftp文件服务器,结束下载");
				redissonClientTemplate.unlock(lockKey);
				this.store.close();
			}
		} catch (Exception e) {
			MySlf4j.textError("定时任务异常处理{0}", MySlf4j.ExceptionToString(e));
		}
	}

	/**
	 * 功能描述 未读的邮件
	 * @return javax.mail.Message[]  多份邮件内容
	 * @author 马庆智
	 * @date 2021/9/9 15:37
	 */
	public Message[] getUnReadMessages() throws Exception {
		long currentTime = System.currentTimeMillis();
		/**在这个基础上加上减去2分钟*/
		currentTime -= config.scanCron * 60 * 1000;
		/**格式化时间，获取到的就是当前时间2分钟前的时间*/
		Date date = new Date(currentTime);
		SearchTerm ComparisonTermGE = new SentDateTerm(ComparisonTerm.GE, date);
		SearchTerm ComparisonTermLE = new SentDateTerm(ComparisonTerm.LE, new Date());
		SearchTerm dateTerm = new AndTerm(ComparisonTermGE, ComparisonTermLE);

		/**判断是否已读*/
		FlagTerm ft = new FlagTerm(new Flags(Flags.Flag.SEEN), false);

		SearchTerm orTerm = new AndTerm(ft, dateTerm);
		Message[] messages = folder.search(orTerm); //根据设置好的条件获取message
		FetchProfile profile = new FetchProfile();
		profile.add(UIDFolder.FetchProfileItem.UID);
		/**获取未读邮件 */
		folder.fetch(messages, profile);

		return messages;
	}

	/**
	 * 功能描述 获取指定天数之前的邮箱数据
	 * @param number 天数
	 * @param  imapFolder 邮箱folder
	 * @return Message[] 邮件数
	 * @author 马庆智
	 * @date 2021/9/9 15:37
	 */
	public Message[] getDeleteMessages(String number, IMAPFolder imapFolder) throws Exception {
		Date date = DateUtils.strToDate(number, DateUtils.DATE_DEFAULT_FORMAT);
		SearchTerm searchTerm = new SentDateTerm(ComparisonTerm.LE, date);
		Message[] messages = imapFolder.search(searchTerm); //根据设置好的条件获取message
		MySlf4j.textInfo("获取要删除的邮件数量,messagelength:{0}", messages.length);
		return messages;
	}

	/**
	 * 功能描述  处理支付宝账单邮件内容,下载zip文件到ftp
	 * @param message 邮件信息
	 * @author 马庆智
	 * @date 2021/9/9 14:55
	 */
	private void dealAlipayBill(Message message) throws Exception {
		try {
			boolean contain = EmailUtils.getContainerAttachment(message);
			if (contain) {
				try {

					InputStream is = EmailUtils.saveAttachment(message, EmailUtils.decodeText(message.getSubject()),
							folder.getUID(message) + "");

					FileOperateUtil.fileWrite(is,
							config.downloadUrl + "/" + DateUtils.getTime() + "/ALIPAY-" + EmailUtils
									.decodeText(message.getSubject()) + "_" + folder.getUID(message) + ".zip");
				} catch (Exception e) {
					MySlf4j.textError("支付宝下载失败:{0}" , MySlf4j.ExceptionToString(e));
				}
			}

		} catch (Exception e) {
			message.setFlag(Flags.Flag.SEEN, false);
			MySlf4j.textError("邮箱对应的UUID:{0},支付宝附件下载有问题,异常问题为:{1}", folder.getUID(message),
					MySlf4j.ExceptionToString(e));
		}
	}

	/**
	 * 功能描述  处理微信账单邮件内容,下载zip文件到ftp
	 * @param message 邮件信息
	 * @author 马庆智
	 * @date 2021/9/9 14:56
	 */
	private void dealVxBill(Message message) throws Exception {
		/**1.获得邮件发件人*/
		Address[] from = message.getFrom();
		String content = getMailContent(message);
		/**2.截取微信url*/
		if (content.contains("href")) {
			String hrefStr = subString(content, "<a href=\"", "\" style=\"font-size");
			MySlf4j.textInfo("邮箱对应的UUID:{0},href值为:{1}", folder.getUID(message), hrefStr);
			CloseableHttpClient client = null;
			CloseableHttpResponse response = null;
			try {
				/**httpclient通过url远程下载*/
				client = HttpClients.createDefault();
				HttpGet httpget = new HttpGet(hrefStr);
				response = client.execute(httpget);
				HttpEntity entity = response.getEntity();
				InputStream is = entity.getContent();

				FileOperateUtil.fileWrite(is, config.downloadUrl + "/" + DateUtils.getTime() + "/VX-" + EmailUtils
						.decodeText(message.getSubject()) + "_" + folder.getUID(message) + ".zip");
			} catch (Exception e) {
				message.setFlag(Flags.Flag.SEEN, false);
				MySlf4j.textError("邮箱对应的UUID:{0},链接url:{1}下载失败,失败原因:{2}", folder.getUID(message), hrefStr,
						MySlf4j.ExceptionToString(e));
			} finally {
				client.close();
				response.close();
			}
		}
	}

	/**
	 * 功能描述 获取邮件内容
	 * @param message
	 * @return java.lang.String
	 * @author 马庆智
	 * @date 2021/10/13 14:17
	 */
	private String getMailContent(Message message) throws IOException, MessagingException {
		Multipart multipart = (Multipart) message.getContent();
		String type = multipart.getContentType();
		String content = "";
		for (int i = 0; i < multipart.getCount(); i++) {
			if (type.contains("multipart/alternative")) { //HTML (文本和超文本组合)

				content += multipart.getBodyPart(i).getContent().toString();

			} else if (type.contains("text/plain")) { //纯文本

				content += multipart.getBodyPart(i).getContent().toString();

			} else if (type.contains("text/html")) { //HTML标签元素

				content += multipart.getBodyPart(i).getContent().toString();

			} else if (type.contains("multipart/related")) { //内嵌资源 (包涵文本和超文本组合)

				content += multipart.getBodyPart(i).getContent().toString();

			} else if (type.contains("application/")) { //应用附件 (zip、xls、docx等)

				content += multipart.getBodyPart(i).getContent().toString();

			}
		}
		return content;
	}

	/**
	 * 功能描述   截取字符串str中指定字符 strStart、strEnd之间的字符串
	 * @param str  指定字符串
	 * @param strStart 开始的字符串
	 * @param strEnd  结束的字符串
	 * @return java.lang.String 返回字符串
	 * @author 马庆智
	 * @date 2021/9/9 14:58
	 */
	public static String subString(String str, String strStart, String strEnd) {

		/** 找出指定的2个字符在 该字符串里面的 位置 */
		int strStartIndex = str.indexOf(strStart);
		int strEndIndex = str.indexOf(strEnd);

		/** index 为负数 即表示该字符串中 没有该字符 */
		if (strStartIndex < 0) {
			return "字符串 :---->" + str + "<---- 中不存在 " + strStart + ", 无法截取目标字符串";
		}
		if (strEndIndex < 0) {
			return "字符串 :---->" + str + "<---- 中不存在 " + strEnd + ", 无法截取目标字符串";
		}
		/** 开始截取 */
		String result = str.substring(strStartIndex, strEndIndex).substring(strStart.length());
		return result;
	}

}
